package com.starxg.m3u8.parser;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import com.starxg.m3u8.exception.M3U8Exception;
import com.starxg.m3u8.utils.HttpUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * M3U8Parser
 *
 * @author huangxingguang@lvmama.com
 * @date 2020-06-01 19:17
 */
@Slf4j
public class M3U8Parser {
    private static final Object START = new Object();
    public static final String FILE_SCHEME = "file:///";

    private M3U8Parser() {
    }

    private static LinkedList<Object> parse(String text, M3U8 m3u8, boolean ignoreKey) throws Exception {

        LinkedList<Object> list = new LinkedList<>();

        for (Iterator<String> iterator = text.lines().iterator(); iterator.hasNext();) {
            String line = iterator.next();

            if (StringUtils.isBlank(line)) {
                continue;
            }

            if (line.startsWith(M3U8.START_PREFIX)) {
                if (list.peekFirst() == null) {
                    list.addFirst(START);
                    log.info("读取到行首。");
                }
                continue;
            }

            // key
            if (line.startsWith(M3U8Key.PREFIX) && !ignoreKey) {
                log.debug("读取到Key:{}", line);
                list.add(M3U8Key.parse(line, m3u8));

                // item
            } else if (line.startsWith(M3U8Item.PREFIX)) {
                String time = StringUtils.removeEnd(line.split(":")[1], ",");
                String uri = iterator.next();
                log.debug("读取到分片:{},url:{}", line, m3u8.absUrl(uri));
                M3U8Item item = new M3U8Item();
                item.setStatus(M3U8ItemStatus.WAITING);
                if (uri.contains("?")) {
                    item.setName(FilenameUtils.getName(uri.substring(0, uri.indexOf("?"))));
                } else {
                    item.setName(FilenameUtils.getName(uri));
                }
                item.setUrl(m3u8.absUrl(uri));
                item.setTime(new BigDecimal(time));
                list.add(item);

                // end
            } else if (line.startsWith(M3U8.END_PREFIX)) {
                log.info("读取到结束节点。");
                break;
            }
        }

        return list;

    }

    public static M3U8 parse(String url) throws Exception {
        return parse(new URL(url));
    }

    public static M3U8 parse(URL url) throws Exception {
        String text;
        if (url.getProtocol().toLowerCase().startsWith("file")) {
            File file = new File(url.getFile());
            try (var is = new FileInputStream(file)) {
                text = IOUtils.toString(is, StandardCharsets.UTF_8);
            }
        } else {
            text = HttpUtils.get(url.toString());
        }
        return parse(url, text);
    }

    public static M3U8 parse(URL url, String text) throws Exception {

        if (!text.trim().startsWith(M3U8.START_PREFIX)) {
            throw new M3U8Exception("无法识别 [" + url + "] 响应的内容");
        }

        // 判断是否含有多码率
        if (text.contains(M3U8Multi.PREFIX)) {
            return m3u8Multi(text, url);
        }

        // 如果不包含结束节点就是直播流
        if (!text.contains(M3U8.END_PREFIX)) {
            return m3u8Live(url, text, false);
        }

        return m3u8Text(url, text, false);
    }

    public static M3U8 parse(URL url, File file) throws Exception {

        String text;

        try (var is = new FileInputStream(file)) {
            text = IOUtils.toString(is, StandardCharsets.UTF_8);
        }

        return m3u8Text(url, text, false);
    }

    private static M3U8Live m3u8Live(URL url, String body, boolean ignoreKey) throws Exception {
        M3U8Live m3u8 = new M3U8Live(url);
        LinkedList<Object> list = parse(body, m3u8, ignoreKey);
        Entry entry = toEntry(list);
        m3u8.setKey(entry.key);
        m3u8.getItems().addAll(entry.items);
        return m3u8;
    }

    public static M3U8Text m3u8Text(URL url, String body, boolean ignoreKey) throws Exception {
        M3U8Text m3u8 = new M3U8Text(url);
        LinkedList<Object> list = parse(body, new M3U8Text(url), ignoreKey);

        Entry entry = toEntry(list);
        m3u8.setKey(entry.key);
        m3u8.setItems(entry.items);
        m3u8.setRaw(body);

        return m3u8;
    }

    /**
     * 解析成多码率
     *
     * @param text
     *            m3u8
     * @param url
     *            地址
     */
    private static M3U8Multi m3u8Multi(String text, URL url) throws MalformedURLException {
        M3U8Multi m3u8 = new M3U8Multi(url);

        for (Iterator<String> iterator = text.lines().iterator(); iterator.hasNext();) {
            String line = iterator.next();
            if (line.startsWith(M3U8Multi.PREFIX)) {
                String[] split = line.split(":")[1].split(",");

                // 找分辨率
                List<String> list = Arrays.stream(split).filter(e -> {
                    e = e.trim();
                    return e.startsWith("RESOLUTION") || e.startsWith("NAME");
                }).collect(Collectors.toList());

                // 分辨率
                String fbl = StringUtils.remove(list.get(0).split("=")[1], "\"");

                // 改码率的地址
                String u = iterator.next();
                m3u8.getStreams().add(new M3U8Multi.Stream(fbl, m3u8.absUrl(u)));
            }
        }

        return m3u8;
    }

    /**
     * 把list转成entry
     */
    private static Entry toEntry(LinkedList<Object> list) {

        if (list.isEmpty() || list.peekFirst() != START) {
            throw new M3U8Exception("解析M3U8失败");
        }

        Entry entry = new Entry();

        List<Object> keys = list.stream().filter(e -> e instanceof M3U8Key).collect(Collectors.toList());

        // 如果只有一个key，那就不需要下面的操作了，所有分片共用一个key
        if (keys.size() == 1) {
            entry.key = (M3U8Key) keys.get(0);
            keys.clear();
        }

        ListIterator<Object> iterator = list.listIterator();

        // 过滤掉头
        iterator.next();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            if (!(obj instanceof M3U8Item)) {
                continue;
            }

            M3U8Item item = (M3U8Item) obj;
            entry.items.add(item);

            // 如果没有key，下面的不需要走了
            if (keys.isEmpty()) {
                continue;
            }

            // 如果上一个为空就跳过
            if (!iterator.hasPrevious()) {
                continue;
            }

            // 索引上移，再次判断是否有上一个
            iterator.previous();
            if (!iterator.hasPrevious()) {
                continue;
            }

            // 如果有就获取，判断是不是key
            Object prev = iterator.previous();
            if (prev instanceof M3U8Key) {
                item.setKey((M3U8Key) prev);
            }

            iterator.next();
            iterator.next();
        }

        return entry;
    }

    /**
     * M3U8Parser
     *
     * @author huangxingguang@lvmama.com
     * @date 2020-08-19 16:35
     */
    private static class Entry {
        private M3U8Key key;
        private List<M3U8Item> items = new ArrayList<>();
    }
}
